#pragma once

#include <iostream>
#include <mutex>
#include <queue>
#include <unistd.h>
#include <pthread.h>
#include "lockGuard.hpp"

const int gDEFAULT_CAP = 5;

template<class T>
class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return _bq.size() == 0;
    }

    bool isQueueFull()
    {
        return _bq.size() == _capacity;
    }
public:
    BlockQueue(int capacity = gDEFAULT_CAP)
        :_capacity(capacity)
    {
        pthread_mutex_init(&_mtx, nullptr);
        pthread_cond_init(&_empty, nullptr);
        pthread_cond_init(&_full, nullptr);
    }

    void push(const T& in) // 生产者
    {
        lockGuard lockguard(&_mtx); // 上锁
        while (isQueueFull()) // 任务队列满了
            pthread_cond_wait(&_full, &_mtx); // 那就等待消费者来消费
        
        _bq.push(in); // 任务队列不为空, 那就生产任务
        pthread_cond_signal(&_empty); // 阻塞队列中有了数据, 去唤醒消费者
    }

    void pop(T* out)
    {
        lockGuard lockguard(&_mtx);
        while (isQueueEmpty()) // 如果任务队列为空
            pthread_cond_wait(&_empty, &_mtx); // 那就等待生产者生产
        *out = _bq.front();  // 拿出数据
        _bq.pop(); // 消费数据
        pthread_cond_signal(&_full); // 消费了数据,就去唤醒生产者去生产
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mtx);
        pthread_cond_destroy(&_empty);
        pthread_cond_destroy(&_full);
    }
private:
    std::queue<T> _bq; // 阻塞队列
    int _capacity;  // 容量上限
    pthread_mutex_t _mtx; // 通过互斥锁, 保护队列安全
    pthread_cond_t _empty; // 条件变量, 用它表示阻塞队列, 是否为空的条件
    pthread_cond_t _full; // 条件变量, 用它表示bq是否满了 的条件
};
